#pragma once

#include <functional>

#include "../precomp.h"

QFL_NAMESPACE_BEGIN

namespace text
{
	enum utf_bom { none=0,utf8,utf16_le,utf16_be };

	enum utf_bom get_utf_bom(std::istream& in)
	{
		enum utf_bom encoding=none;
		int ch = in.peek();
		if (ch==0xEF)	// utf-8
		{
			in.get();
			ch=in.peek();
			if ( ch == 0xBB )
			{
				in.get();
				ch=in.peek();
				if ( ch==0xBF )
				{
					in.get();
					encoding = utf8;
				}
			}
		}
		else if (ch==0xFF)	// unicode LE
		{
			in.get();
			ch=in.peek();
			if ( ch == 0xFE )
			{
				in.get();
				encoding = utf16_le;
			}
		}
		else if (ch==0xFE)	// unicode BE
		{
			in.get();
			ch=in.peek();
			if (ch==0xFF)
			{
				in.get();
				encoding = utf16_be;
			}
		}
		return encoding;
	}

	inline
	wchar_t get_utf8_char(std::istream& in)
	{
		int ch = in.get();
		if (ch<0xc2)
		{
			return ch;
		}
		else if (ch<0xd0)
		{
			int ch1 = in.get();
			return ((ch&0x1f)<<6) | (ch1&0x3f);
		}
		else if (ch<0xf0)
		{
			int ch1 = in.get();
			int ch2 = in.get();
			return ((ch&0xf)<<12) | ((ch1&0x3f)<<6) | (ch2&0x3f);
		}
		return ch;
	}

	inline
	wchar_t get_utf16le_char(std::istream& in)
	{
		wchar_t wch;
		char* p = reinterpret_cast<char*>(&wch);

		p[0]=in.get();
		p[1]=in.get();
		return wch;
	}

	inline
	wchar_t get_utf16be_char(std::istream& in)
	{
		wchar_t wch;
		char* p = reinterpret_cast<char*>(&wch);
		p[1]=in.get();
		p[0]=in.get();
		return wch;
	}
	/////////////////////////////////////////////////////////////////
	inline
	std::ostream& put_utf_bom(std::ostream& out,enum utf_bom encoding)
	{
		switch(encoding)
		{
		case utf8:
			out.put('\xef').put('\xbb').put('\xbf');
			break;
		case utf16_le:
			out.put('\xff').put('\xfe');
			break;
		case utf16_be:
			out.put('\xfe').put('\xff');
			break;
		}
		return out;
	}

	inline
	std::ostream& put_utf8_char(std::ostream& out,wchar_t ch)
	{
		if (ch<0x80)
		{
			out.put(char(ch));
		}
		else if (ch<0x800)
		{
			out.put((ch>>6)|0xc0);
			out.put((ch&0x3f)|0x80);
		}
		else
		{
			out.put((ch>>12)|0xe0);
			out.put((ch>>6)&0x3f|0x80);
			out.put(ch&0x3f|0x80);
		}
		return out;
	}

	inline
	std::ostream& put_utf16le_char(std::ostream& out,wchar_t ch)
	{
		out.put(ch&0xff);
		out.put(ch>>8);
		return out;
	}

	inline
	std::ostream& put_utf16be_char(std::ostream& out,wchar_t ch)
	{
		out.put(ch>>8);
		out.put(ch&0xff);
		return out;
	}

	/////////////////////////////////////////////////////////////////
	inline
	std::pointer_to_unary_function<std::istream&,wchar_t>
	fun_get_char(enum utf_bom encoding)
	{
		switch(encoding)
		{
		case utf16_le:
			return std::ptr_fun(get_utf16le_char);
		case utf16_be:
			return std::ptr_fun(get_utf16be_char);
		default:
			return std::ptr_fun(get_utf8_char);
		}
	}

	inline
	std::pointer_to_binary_function<std::ostream&,wchar_t,std::ostream&>
	fun_put_char(enum utf_bom encoding)
	{
		switch(encoding)
		{
		case utf16_le:
			return std::ptr_fun(put_utf16le_char);
		case utf16_be:
			return std::ptr_fun(put_utf16be_char);
		default:
			return std::ptr_fun(put_utf8_char);
		}
	}

//end namespace 
}
QFL_NAMESPACE_END
